#!/bin/env python
"""Package for scheduling within individual Sensors.

Copyright (C) Sarah Mount, 2005.

This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA


This contains the Scheduler and Task objects used by each sensor
to preform its tasks at the correct time
"""

__author__  = "Sarah Mount"
__date__    = "2005/01/07 15:11:17"

        
class Scheduler(object):
        """
        Schedules a list of tasks.
        This returns a priority queue for any tasks a sensor may have.
        Priorities start at zero. Lower numbers have higher priority.
        """
        def __init__(self, tasklist, n=-1):
                """Initialise a Scheduler object

		@type tasklist: list
		@param tasklist: a list of tasks to schedule
		@type n: int		
		@param n: Number of times to repeat the scheduling process
		"""
		self.n = n
                self.slist = tasklist
                self.slist.sort(lambda x, y: cmp(x.priority, y.priority))

        def addTask(self,task):
                """Lets the user add a task at runtime after the list has been completed.
                
                @param task: The task to add to the list
                """
                self.slist.append(task)
                #self.slist = tasklist
                self.slist.sort(lambda x, y: cmp(x.priority, y.priority))
                
        def removeTask(self,task):
                """Removes a task from the scheduler.
                
                @param task: The task to remove
                """
                if task in self.slist:
                        self.slist.remove(task)
                        
                
        def __run(self):
                """Run through the priority queue once."""
                for i in self.slist:
                        if i.repetition.schedule():
                                i.task()
                return
        def run(self, n=-1):
                """Run the priority queue n times.

                @param n: The number of times to Run. If n<0 go forever.
                """
                if self.n<0:
                        while 1:
                                self.__run()
                else:
                        while self.n != 0:
				#print "Running %s" %self.n
                                self.__run()
                                self.n -= 1
                return
        
        def __repr__(self):
                """Return the scheduler as a formatted String

		@rtype: str
		"""
                t_list = ""
                for i in self.slist:
                        t_list += i.__repr__()
                        t_list += "\n\t"
                return "Scheduler[\n\t" + t_list + "]"
	
	def canRun(self):
		"""Whether or note the scheduler has any tasks to Run
		@rtype: bool
		"""
		return self.n != 0

class Task(object):
        """Naive description of a sensor task.
        
        Priorities start at zero. Lower numbers have higher priority.

        If the repetition argument is less than zero a Task is never run,
        if it is equal to zero the task is run every time the scheduler reaches it's slot.
        If the argument is n (with n>0), then the Task is run every n times the scheduler reaches it.
        C{
        type(priority)   == type(0)
        type(repetition) == type(0)
        }
        """
        
        NegativePriority = "A sensor cannot have priority < 0"

        def __init__(self, priority, repetition, sensor):
                """Create a new task object.
                
                This creates a new task to be added to the scheduler.

                @type priority: int
                @param priority: The tasks priority Higher priorities will be executed before lower priories
		@type repetition: int
                @param repetition: If 0 Run every time the scheduler is run.  Otherwise run every n times the scheduler is called
		@type sensor: str
                @param sensor: The Sensor that preforms the task
                           
                """
                if priority < 0:
                        raise Task.NegativePriority
                self.priority   = priority
                self.repetition = Repeat(repetition)
                self.sensor     = sensor
                
        def __repr__(self):
                return ("Task[priority: " + str(self.priority) +
                        ",  repetition: " + self.repetition.__repr__() + " Sensor " + self.sensor + "]")
                        

class Repeat(object):
        """Repeat objects are used by the scheduler to determine when a Task is run.
        If the integer argument to __init__ is less than zero a Task is never
        run, if it is equal to zero the task is run every time the scheduler
        reaches it's slot. If the argument is n (with n>0), then the Task
        is run every n times the scheduler reaches it.
        """
        def __init__(self, n):
                """ Initialise a Repeat object.
                This creates a new repeat object used in the #Scheduler class. A repeat object 
                informs the scheduler how often to run a given task

                @type n: int
                @param n: How often the scheduler runs the task. 0 = Every time, N Every Nth time
                """
                self.n = n
                self.timer = 0
                return
        def schedule(self):
                """Calculate if the task is to be run this cycle.
                This is a helper method for the scheduler that calculates if a task is to be run 
                this scheduler cycle, based on the value of n given at initialisation.

		@rtype: int
                """
                if self.n < 0:
                        return 0
                if self.n == 0:
                        return 1
                if self.timer == 0:
                        self.timer = self.n - 1
                        return 1
                self.timer -= 1
                return 0
        def __repr__(self):
                """ Return a formatted string representing the repeat item."""
                return ("Repeat("+str(self.n)+")")


if __name__ == "__main__":   
        class ConcreteTask(Task):
                """ Test class for the Scheduler
                Concrete task with a 'real' algorithm.
                Used for testing the Task method
                """
                def __init__(self,priority, repetition,sensor):
                        self.count = 0
                        Task.__init__(self, priority, repetition,sensor)
                        return
                def task(self):
                        self.count += 1
                        print self.__repr__()
                        return
                def __repr__(self):                        
                        return ("%s ConcreteTask[priority: " + str(self.priority) +",repetition: " + self.repetition.__repr__() + " Sensor: " +self.sensor + " Count:" +str(self.count) +"]")

        deleteTask = ConcreteTask(1,0,"DELETE")
       
        l = [deleteTask,ConcreteTask(1,0,"Sensor[1]"), #Should run priorty 1 Every time
             ConcreteTask(2,2,"Sensor[2]"), #Priority 2 Every other time
             ConcreteTask(0,1,"Sensor[3]"), #Prioiry 0 Every other time
             ]

        extraTask = ConcreteTask(1,0,"Sensor[99")
        
        s = Scheduler(l)
        n = 2
        #print "Testing", s.__class__
        print "Task queue looks like this:"
        print s.__repr__()
        print "Running task queue", str(n), "times..."
        s.run(n)
        print "===============Adding Task================"
        s.addTask(extraTask)
        print "Task queue looks like this:"
        print s.__repr__()
        print "Running task queue", str(n), "times..."
        s.run(n)
        print "================REMOVEING TASK=============="
        s.removeTask(deleteTask)
        print "Task queue looks like this:"
        print s.__repr__()
        print "Running task queue", str(n), "times..."
        s.run(n)
